package com.mk.settlement.service.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.mk.settlement.common.FeeUtil;
import com.mk.settlement.enums.DefaultRelationEnums;
import com.mk.settlement.enums.FeeTypeEnums;
import com.mk.settlement.enums.SaleTypeEnums;
import com.mk.settlement.enums.SettlementCategoryEnums;
import com.mk.settlement.enums.SettlementStateEnums;
import com.mk.settlement.enums.SettlementTargetIDEnums;
import com.mk.settlement.enums.SettlementTypeEnums;
import com.mk.settlement.mappers.HotelModelMapper;
import com.mk.settlement.mappers.SettlementOrderMapper;
import com.mk.settlement.mappers.SettlementRelationMapper;
import com.mk.settlement.mappers.SettlementResultMapper;
import com.mk.settlement.mappers.SettlementRuleMapper;
import com.mk.settlement.mappers.SettlementTempResultMapper;
import com.mk.settlement.model.HotelModel;
import com.mk.settlement.model.SettlementOrder;
import com.mk.settlement.model.SettlementRelation;
import com.mk.settlement.model.SettlementResult;
import com.mk.settlement.model.SettlementRule;
import com.mk.settlement.model.SettlementTempResult;
import com.mk.settlement.service.SettlementService;

/**
 * 结算service实现类
 * @author lm
 *
 */
@Service(value="settlementServiceImpl")
public class SettlementServiceImpl implements SettlementService {
    
    private Logger log = LoggerFactory.getLogger(SettlementServiceImpl.class);
	
	@Autowired 
	private SettlementRuleMapper settlementRuleMapper;
	
	@Autowired
	private SettlementRelationMapper settlementRelationMapper;
	
	@Autowired
	private SettlementOrderMapper settlementOrderMapper;
	
	@Resource
    private SettlementResultMapper settlementResultMapper;
	
	@Resource
    private SettlementTempResultMapper settlementTempResultMapper;
	
	@Resource
    private HotelModelMapper hotelModelMapper;
	
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
	
	@Override
	//规则初始化
	public void addSettlementRule(SettlementRule settlementRule) {
		settlementRuleMapper.insertSelective(settlementRule);
	}

	@Override
	//规则映射关系初始化
	public int addSettlementRelation(SettlementRelation settlementRelation) {
		return settlementRelationMapper.insert(settlementRelation);
	}

	@Override
	//订单基础表添加数据
	public void addSettlementOrder(SettlementOrder settlementOrder) {
		try {
			settlementOrderMapper.insert(settlementOrder);
		} catch (DuplicateKeyException e) {
            log.error("向订单基础表添加数据异常!", e);
        }
	}

	@Override
	//根据hotelid 或 otaid 查询结算规则
	public List<SettlementRule> getSettlementRulesByTarId(Long settlementTargetId, String payType) {
		return settlementRuleMapper.getSettlementRulesByTarId(settlementTargetId, payType);
	}

    @Override
    public boolean reSettlement(Long settlementTargetId, FeeTypeEnums feeType, Date settlementTime, SaleTypeEnums saleType,
            SettlementCategoryEnums settlementCategory, SettlementTypeEnums settlementType) {

        log.info("重新结算开始...");
        
        String formattedSettlementTime = sdf.format(settlementTime);
        
        log.info("参数列表[settlementTargetId" + ":" + settlementTargetId + "],[feeType:" + feeType + "],[settlementTime:" + formattedSettlementTime
                + "],[saleType:" + saleType + "],[settlementCategory:" + settlementCategory + "],[settlementType:" + settlementType + "].");

        int pFeeType = feeType != null ? feeType.getIndex() : 0;
        int pSaleType = saleType != null ? saleType.getIndex() : 0;
        int pSettlementCategory = settlementCategory != null ? settlementCategory.getIndex() : 0;
        int pSettlementType = settlementType != null ? settlementType.getIndex() : 0;
        
        try {
            int dResult = settlementResultMapper.deleteByParams(settlementTargetId, pFeeType, formattedSettlementTime, pSaleType,
                    pSettlementCategory, pSettlementType);
            log.info("删除已生成结果数据" + dResult + "条.");
        } catch (Exception e) {
            log.error("删除已生成结果数据异常!", e);
            
            throw e;
        }
        
        try {
            int iResult = settlementResultMapper.settlementByParams(settlementTargetId, pFeeType, formattedSettlementTime, pSaleType,
                    pSettlementCategory, pSettlementType);
            log.info("重新生成结算数据" + iResult + "条.");
        } catch (Exception e) {
            
            log.error("重新生成结算数据异常!", e);
            
            throw e;
        }
        
        log.info("重新结算完成.");
        
        return true;
    }

    /**
     * 账单到达阈值后,酒店老板可触发结算生成账单
     */
    @Override
    public boolean settlementByBoss(Long settlementTargetId) throws Exception{
        
        log.info("老板触发阈值结算,结算酒店id:" + settlementTargetId);
        
        List<SettlementTempResult> results = getCanSettlementTemp(settlementTargetId);
        
        if(results == null) {
            
            log.info("该酒店[" + settlementTargetId + "]未达到结算阈值,不能结算.");
            
            return false;
        }
        
        List<Long> ids = new ArrayList<Long>();
        
        for(SettlementTempResult result : results) {
            
            ids.add(result.getId());
        }
        
        log.info("应插入结果表数据id=>" + ids);
        
        try {
            int iResult = settlementResultMapper.insertResultFromTempById(ids, sdf.format(new Date()));
            if (iResult == ids.size()) {
                log.info("数据从临时表添加至结果表完毕.");
            } else {
                log.info("数据从临时表添加至结果表失败.");
                
                throw new Exception("添加至结果表数据条数少于应添加条数!");
            }
        } catch (Exception e) {
            log.error("数据从临时表添加至结果表异常!", e);
            
            throw e;
        }
        
        try {
            int uResult = settlementTempResultMapper.updateStatusAndEndTime(SettlementStateEnums.AlreadyStatistics.getIndex(), ids, sdf.format(new Date()));
            if (uResult == ids.size()) {
                log.info("更新临时表数据完毕.");
            } else {
                log.info("更新临时表数据失败.");

                throw new Exception("更新结果表数据条数少于应更新条数!");
            }
        } catch (Exception e) {
            log.error("更新临时表数据异常!", e);
            
            throw e;
        }
        
        log.info("老板触发阈值结算完毕.");
        return true;
    }

    /**
     * 重新生成结算数据
     */
    @Override
    public boolean reSettlement(SettlementResult settlementResult) {
        
        log.info("重新生成结算...");
        Long id = settlementResult.getId();
        log.info("需要重新计算的数据:" + "id =>" + id);
        log.info("输入参数=>" + settlementResult.toString());
        
        if (settlementResult.getSettlementTargetId() >= SettlementTargetIDEnums.TRAVEL_AGENCY.getBegin()
                && settlementResult.getSettlementTargetId() <= SettlementTargetIDEnums.TRAVEL_AGENCY.getEnd()) {
            
            log.info("需要重新结算的是旅行社,结算价格按阶梯价格计算.");
            
            try {
                BigDecimal sum = settlementResultMapper.getSumFromOrder(settlementResult);
                SettlementResult sr = new SettlementResult();
                sr.setId(settlementResult.getId());
                sr.setSettlementTotalSum(sum);
                sr.setSettlementSaleSum(FeeUtil.cal(sum, 0));
                int result = settlementResultMapper.updateByPrimaryKeySelective(sr);
                if (result > 0) {

                    log.info("id:" + id + "重新生成结算成功.");
                    return true;
                } else {
                    log.error("重新生成结算,id =>" + id + "失败.");

                    return false;
                }
            } catch (Exception e) {
                log.error("重新生成结算,id =>" + id + "异常!", e);
                return false;
            }

        } else {
            
            try {
                
                int result = settlementResultMapper.reSettlementThreshold(settlementResult);
                if (result > 0) {

                    log.info("id:" + id +"重新生成结算成功.");
                    return true;
                } else {
                    log.error("重新生成结算,id =>" + id + "失败.");
                    
                    return false;
                }
            } catch (Exception e) {
                
                log.error("重新生成结算,id =>" + id + "异常!", e);
                return false;
            }
        }
        
        
    }

    /**
     * 
     */
    @Override
    public String canSettlement(Long settlementTargetId) {
        
        List<SettlementTempResult> results = getCanSettlementTemp(settlementTargetId);
        
        if(results == null) {
            return "fail";
        }
        
        return "success";
    }

    private List<SettlementTempResult> getCanSettlementTemp(Long settlementTargetId) {
        
        List<SettlementTempResult> results = null;
        try {
            results = settlementTempResultMapper.getNoStatisticsTempByTargetId(settlementTargetId);
        } catch (Exception e) {
            
            log.error("根据酒店id查询为结算累计金额异常!", e);
            
            return null;
        }
        
        if(CollectionUtils.isEmpty(results)) {
            
            return null;
        }
        
        BigDecimal income = new BigDecimal(0);
        BigDecimal expenditure = new BigDecimal(0);
        
        for(SettlementTempResult result :results) {
            
            if(result.getSettlementCategory() == SettlementCategoryEnums.INCOME.getIndex()) {
                
                income = result.getSum().add(income);
            } else {
                expenditure = result.getSum().add(expenditure);
            }
        }
        
        BigDecimal threshold;
        try {
            threshold = settlementRelationMapper.getThresholdByTargetId(settlementTargetId);
        } catch (Exception e) {
            
            log.error("根据酒店id查询结算阈值异常!", e);
            
            return null;
        }
        
        if(threshold == null) {
            
            log.error("酒店id=>" + settlementTargetId + "没有获取到结算阈值.");
            
            return null;
        }
        
        //达到阈值
        if(threshold.compareTo(expenditure.subtract(income)) == -1) {
            
            return results;
        }
        
        return null;
    }

    @Override
    public String initRelation(int ruleId) {
        
        List<HotelModel> hotels = hotelModelMapper.getAll();

        log.info("共需要初始化" + hotels + "条酒店数据...");
        int total = 0;

        for (HotelModel h : hotels) {

            SettlementRelation r = new SettlementRelation();

            r.setSettlementRuleId(ruleId);
            r.setSettlementTargetId(h.getId());
            r.setSettlementTargetName(h.getHotelname());
            r.setCreateTime(new Date());
//            r.setSettlementThreshold(new BigDecimal(1000).setScale(2));

            try {
                int iResult = settlementRelationMapper.insert(r);
                if (iResult > 0) {
                    total++;
                }
            } catch (DuplicateKeyException e) {
                log.info("酒店id:" + h.getId() + "已经存在于规则" + ruleId +"的对应关系,继续.");
            } catch (Exception e) {
                log.error("插入酒店规则对应关系异常!", e);
            }
            
        }

        log.info("共初始化" + total + "条数据.");
        
        return "success";
    }

    @Override
    public int insertDefaultRelation(Long settlementTargetId, String settlementTargetName, String mark) {

        log.info("settlementTargetId:" + settlementTargetId + ",settlementTargetName" + settlementTargetName + ",mark:" + mark
                + ",使用默认规则初始化关系...");
        
        int total = 0;
        List<Integer> rules = DefaultRelationEnums.getDefaultRuleId(mark);

        if (CollectionUtils.isEmpty(rules)) {

            return 0;
        } else {

            for (Integer rule : rules) {

                log.info("settlementTargetId:" + settlementTargetId + ",settlementTargetName" + settlementTargetName + ",mark:" + mark + ",使用默认规则"
                        + rule + "初始化关系.");
                SettlementRelation r = new SettlementRelation();

                r.setSettlementTargetId(settlementTargetId);
                r.setSettlementTargetName(settlementTargetName);
                r.setSettlementRuleId(rule);

                try {
                    int iResult = settlementRelationMapper.insert(r);
                    if (iResult > 0) {
                        total++;
                    }
                } catch (Exception e) {
                    log.info("settlementTargetId:" + settlementTargetId + ",settlementTargetName" + settlementTargetName + ",mark:" + mark
                            + ",使用默认规则插入关系表时异常!");
                }
            }

            log.info("settlementTargetId:" + settlementTargetId + ",settlementTargetName" + settlementTargetName + ",mark:" + mark + ",使用默认规则插入关系表"
                    + total + "条数据.");
        }

        return total;
    }

    @Override
    public List<SettlementRule> getRulesByIds(List<Integer> ruleIds) {
        
        List<SettlementRule> rules = null;
        
        try{
            log.info("根据规则id" + ruleIds+ "获取规则列表...");
            rules = settlementRuleMapper.getRulesByIds(ruleIds);
        } catch (Exception e) {
            
            log.info("根据规则获取规则列表异常!", e);
        }
        
        return rules;
    }

}
